MFC三种绘图模式六种图形对象

早期开发图形程序都是直接针对具体设备进行的,要开发一个图形软件必须先了解是什么型号的显示卡或者打印机,根据每个厂家提供的接口编写不同的代码来开发。进入Windows时代,操作系统通过对驱动程序的统一管理,将设备接口细节隐藏于操作系统内部。程序员在编写图形程序时,只要调用一个公用的虚拟设备即可,这个虚拟设备也就是DC。

在Windows应用程序中,设备环境(DC,绘图模式)与图形对象协同进行绘图显示工作。就像画家绘图一样,设备环境(绘图模式)好比是画家的画布,图形对象好比是画家的画具,画家可以使用不同的画布、不同的画笔、画刷、颜料等,画出不同色彩、不同线条 、不同材料的画。

MFC绘图类包括绘图环境类和绘图对象类。

1 绘图环境类(绘图模式)

绘图环境类是MFC对GDI三类绘图模式函数封装成CDC及三种派生类。

CDC派生类封装的GDI函数及功能说明:

a) CPaintDC类:封装了BeginPaint和EndPaint函数。

用于标准客户区绘图,窗口刷新时不消失,在WM_PAINT消息下使用。

b) CWindowDC类:封装了GetWindowDC和ReleaseDC函数。

用于非客户区绘图,在WM_NCPAINT消息下使用,窗口刷新时不消失。

c) CClientDC类:封装了GetDC和ReleaseDC函数。

用于临时客户区绘图,窗口刷新时消失,任何时候都可以使用。

d) CMemDC类:封装CreateCompatibleDC和DeleteDC等函数。

VC6.0暂未封装该类,VS 2010有封闭。在内存中绘图,常用于双缓冲防止画面闪烁。

CMetaFileDC类-父类是CDC类,作用保存绘制命令。

实例:

void CXdcDlg::OnPaint()

{

CPaintDC dc(this); // device context for painting

dc.Rectangle(0,0,40,40);

dc.Rectangle(80,80,120,120);

dc.MoveTo(20,20);

dc.LineTo(100,100);

}

void CXdcDlg::OnNcPaint()

{

#if 0

CWindowDC dc(this);

CRect rect;

GetWindowRect(rect);

dc.Rectangle(0,0,rect.Width(),rect.Height());

char str[222];

sprintf(str,"非客户空间(%d,%d,%d,%d)",rect.left,rect.top,rect.right,rect.bottom);

dc.SetTextColor(RGB(255,0,0));

dc.TextOut(5,5,str,strlen(str));

#endif

}

void CXdcDlg::OnLButtonDown(UINT nFlags, CPoint point)

{

CClientDC dc(this);

dc.Ellipse(point.x-10,point.y-10,point.x+10,point.y+10);

CDialog::OnLButtonDown(nFlags, point);

}

图片描述(最多50字)

图片描述(最多50字)

2 绘图对象类

使用GDI对象可以改变线条的颜色、大小,可以使用填充颜色,改变字体,显示一张位图等。不过,GDI对象是要通过DC才能发生作用的。要使用这些GDI对象,必须使用SelectObject函数将其选入DC中。

主要有六种,全部由CGdiObject类派生:

CPen-画笔,用于修饰线条的颜色、宽度和线型(实线、虚线或点划线等);

CBrush-画刷,用于修饰一个闭合图形内部的填充内容(填充颜色、填充线条以及平铺位图;

CFont-字体,用于修饰输出文字的样式、高度、粗细以及字间距等;

CBitmap-位图,用于显示和处理点阵图像;

CRgn-区域,用于修饰输出图像的不规则边界,如椭圆或多边形等;

CPalette-调色板,用于修饰输出图像的色调,如16色或256色等;

颜色变量和处理函数:

typedef DWORD COLORREF;

COLORREF c1 = RGB(255,255,255);

一般按如下步骤使用GDI对象:

a、创建或得到一个GDI对象;

b、使用dc.SelectObject函数把GDI对象选入DC;

c、使用DC进行绘图或文字输出;

d、恢复DC原来的GDI对象并删除刚新创建的GDI对象;

3 一般的使用GDI对象的绘图步骤

综合DC和GDI对象的使用步骤,则绘图的完整步骤为:

3.1 获取或者创建一个DC,也就是创建一个绘图模式或环境;

3.2 获取或者创建一个GDI对象(Pen, Brush等);

3.3 使用dc.SelectObject函数把GDI对象选入DC;

3.4 使用DC进行绘图或文字输出;

3.5 恢复DC原来的GDI对象并删除刚新创建的GDI对象,如pen.DeleteObject();

3.6 释放或删除设备描述表DC;

其中,1和6,2和4是对应的。

4 CPen类(画笔)

画笔决定了线条的颜色、宽度和线型(实线、点线或点划线等)。Windows使用当前在设备描述表中已选择的画笔来画线。程序中可以选择Windows的预定义画笔,也可以选择自定义的画笔。

预定义画笔有三种:BLACK_PEN(黑色笔) 、WHITE_PEN(白色笔)和NULL_PEN(空笔),这些都在windows.h中已经定义好了,程序员可使用GetStockObject函数来选择其中的一种,系统缺省的画笔为黑色笔。Windows.h包含了HPEN的数据类型定义,使用该类型可以定义画笔句柄的变量。

仅靠系统提供的预定义画笔远远不能满足需求,应用程序可根据实际需要创建一种自定义的逻辑画笔。其步骤一般为:首先用CreatePen或CreatePenIndirect函数建立一支画笔,再调用SelectObject函数将其选入设备描述表,此后就可使用该画笔在选定的设备描述表中进行绘图操作。任何时候某一设备描述表只能有一支画笔被选入作为当前画笔,当一支画笔被选入时,原先已选入的画笔便不再有效。完成绘图操作后,可以通过调用DeleteObject来释放已建立的画笔。

* 函数CreatePen()

语法:HPEN CreatePen(int fnPenStyle,int nWidth,COLORREF clrref);

说明:该函数创建一个逻辑画笔。其中fnPenStyle参数指定画笔的线型,该参数可取由windows.h定义的七个标识符之一,其含义为:

PS_SOLID 实线

PS_DASH 虚线

PS_DOT 点线

PS_DASHDOT 夹一点虚线

PS_DASHDOTDOT 夹二点虚线

PS_NULL 无

PS_INSIDEFRAME 线画在所有构件框架内

nWidth参数是用逻辑单位表示的画笔的宽度;

clrref参数是一个COLORREF类型的颜色值,指定画笔的颜色,可用宏指令RGB构造这个值,如:clrref=RGB(byRed,byGreen,byBlue);

实例

void CPenDlg::OnPaint()

{

CPaintDC dc(this); // 绘图模式之一(标准客户区绘图),另外还有CWindowDC(非客户区)、CClientDC(临时客户区)

dc.TextOut(20,20,"默认画笔:黑色,宽度为1");

dc.MoveTo(20,60);

dc.LineTo(120,60);

dc.TextOut(20,80,"通过构造函数创建画笔:CPen pn1(PS_SOLID,4,RGB(255,0,0));");

CPen pn1(PS_SOLID,4,RGB(255,0,0));

CPen *pOldPen = dc.SelectObject(&pn1); //选择的是新建画笔,返回的是之前的旧画笔

dc.MoveTo(20,120);

dc.LineTo(120,120);

dc.TextOut(20,140,"通过CreatePen函数创建画笔:pn2.CreatePen(PS_DASHDOT,1,RGB(0,0,255));");

CPen pn2;

pn2.CreatePen(PS_DASHDOT,1,RGB(0,0,255));

dc.SelectObject(&pn2);

dc.MoveTo(20,180);

dc.LineTo(120,180);

dc.TextOut(20,200,"画一个矩形dc.Rectangle(20,240,140,300); 右上、右下角坐标 ");

dc.SelectObject(pOldPen); // 选择默认画笔

dc.Rectangle(20,240,140,300);

dc.TextOut(20,320,"画一个无边框的矩形:CPen pn3(PS_NULL,1,RGB(0,0,0));");

CPen pn3(PS_NULL,1,RGB(0,0,0));

dc.SelectObject(&pn3);

dc.Rectangle(20,360,140,420);

dc.TextOut(20,440,"等价的无边框矩形的画法:dc.FillSolidRect(20,440,120,60,RGB(255,255,255));右上角坐标,长,宽");

dc.FillSolidRect(20,480,120,60,RGB(255,255,255)); //右上角坐标,长,宽

dc.SelectObject(pOldPen); // 恢复默认画笔

}

运行效果:

图片描述(最多50字)

5 CBrush(刷子)

当我们在绘制一些区域图形时,其内部往往需要以某种图案进行填充,这就需要选定"刷子"作为绘图工具。Windows系统不仅为用户提供了预定义刷子,而且还允许应用程序自定义刷子。

Windows系统中预定义的刷子有如下七种:

BLACK_BRUSH 黑色刷子

DKGRAY_BRUSH 深灰色刷子

GRAY_BRUSH 灰色刷子

HOLLOW_BRUSH 中空刷子,画边界而不填充

LTGRAY_BRUSH 浅灰色刷子

NULL_BRUSH 空刷子

WHITE_BRUSH 白色刷子

应用程序可以调用GetStockObject函数选用其中一个,系统缺省的刷子是白色刷子。Window.h包含了HBRUSH数据类型的定义,使用该类型就可定义刷子句柄的变量。

仅靠这七种刷子往往不能满足要求,应用程序通过调用如下几种函数创建逻辑刷子,这些函数返回值均为刷子句柄。

* 函数CreateHatchBrush()

语法:HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref);

说明: 该创建一个带阴影的逻辑刷子。

FnStyle指定的阴影格式如下:

HS_BDLAGONAL 45度向上斜线组成的阴影图案(自左到右)

HS_CROSS 水平和垂直交叉组成的阴影图案

HS_DIAGCROSS 45度斜线交叉组成的阴影图案

HS_FDIAGONAL 45度向下斜线组成的阴影图案(自左到右)

HS_HORZONA 水平线组成的阴影图案

HS_VERTICAL 垂直线组成的阴影图案

Clrref是具有COLORREF类型定义的刷子颜色值,可用宏指令RGB构造这个值。

* 函数CreateSolidBrush()

语法:HBRUSH CreateSolidBrush(COLORREF clrre);

说明:该函数创建的是一种实心颜色的逻辑刷子。clrre含义同上。

同样,使用创建刷子的函数时,要检查其返回,确保它是一个有效的句柄。

一旦创建了绘图工具之后,可以SelectObject函数把它选择到显示缓冲区里。

在使用显示缓冲区之前,并不一定非要创建和选择绘图工具,Windows为每个显示缓冲区提供默认的绘图工具。例如:黑色笔,白色刷子和系统字体。

DeleteObject函数用来删除不再需要的绘图工具,但不能删除一个已选进显示缓冲区的绘图工具,而是应该使用SelectObject函数恢复原有的绘图工具,然后再删除需要删除的工具。

6 CFont(字体)

Windows是使用定义好的与设备无关的字符集,Windows的"文本"字符也是图形,所以屏幕上所显示的用打印机或绘图仪等输出品的文本完全一样,做到"所见即所得"。

文本绘制函数有:

TextOut 以当前的字体写一字符串

DrawText 在一个特定矩形区中绘制某一格式的文本

ExtTextOut 在一个特定矩形区中,以当前字体写一字符串

GrayString 用灰色文本写一字符串

TabbedTextOut 写一带扩展字符的字符串

要输出文本就离不开字体。获取字体的相关信息可以使用函数:

BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics )

结构TEXTMETRIC的定义如下所示:

typedef struct tagTEXTMETRIC { // tm

LONG tmHeight; //字符高度

LONG tmAscent; //字符上部高度(基线以上)

LONG tmDescent; //字符下部高度(基线以下)

LONG tmInternalLeading; //由tmHeight定义的字符高度的顶部空间数目

LONG tmExternalLeading; //加在两行之间的空间数目

LONG tmAveCharWidth; //平均字符宽度

LONG tmMaxCharWidth; //最宽字符的宽度

LONG tmWeight; //字体的粗细轻重程度

LONG tmOverhang; //加入某些拼接字体上的附加高度

LONG tmDigitizedAspectX; //字体设计所针对的设备水平方向

LONG tmDigitizedAspectY; //字体设计所针对的设备垂直方向

BCHAR tmFirstChar; //为字体定义的第一个字符

BCHAR tmLastChar; //为字体定义的最后一个字符

BCHAR tmDefaultChar; //字体中所没有字符的替代字符

BCHAR tmBreakChar; //用于拆字的字符

BYTE tmItalic; //字体为斜体时非零

BYTE tmUnderlined; //字体为下划线时非零

BYTE tmStruckOut; //字体被删去时非零

BYTE tmPitchAndFamily; //字体间距(低4位)和族(高4位)

BYTE tmCharSet; //字体的字符集

} TEXTMETRIC;

GDI字体族和字样表如下表所示:

字体族字体族常量字样说明

Dontcare FF_DONTCARE System 当不能提供字体信息或字体并不

重要时使用

Decorative FF_DECORATIVE Symbol 新奇字体

Modern FF_MODERN Courer,ModernIerminal 笔画大小固定的字体,但衬线可有可无

Roman FF_ROMAN Roman,TimeRoman 有衬线的、笔画大小可变的罗马字体

Script FF_SCRIPT Script 仿手写体

Swiss FF_SWISS Helvetical,System 无衬线的、笔画大小可变的字体

* CreateFontIndirect函数

语法: HFONT CreateFontIndirect(

CONST LOGFONT *lplf // pointer to logical font structure

);

说明:参数lplf是LOGFONT结构的指针。结构中含有逻辑字体的特征信息。该函数用lplf所指的LOGFONT结构中的信息创建一种逻辑字体。LOGFONT结构的定义如下:

typedef struct tagLOGFONT { // lf

LONG lfHeight; //字高度

LONG lfWidth; //字符平均宽度

LONG lfEscapement; //行与水平页角度

LONG lfOrientation; //基线与水平角度

LONG lfWeight; //笔划的粗细

BYTE lfItalic; //非零为斜体

BYTE lfUnderline; //非零为下划线

BYTE lfStrikeOut; //非零为中划线

BYTE lfCharSet; //指定字符集

BYTE lfOutPrecision; //输出精度

BYTE lfClipPrecision; //裁剪精度

BYTE lfQuality; //输出质量

BYTE lfPitchAndFamily; //字体的字距和族

TCHAR lfFaceName[LF_FACESIZE]; //含字体名的字符串

} LOGFONT;

* 函数SetTextAlign

大多数文本函数传递的参数表都要求有一个点坐标参数以定义写文本的参考点。当前文本对齐属性规定了字符串如何相对于所传递的坐标进行写。SetTextAlign函数用以设置当前文本对齐属性。

语法:

UINT SetTextAlign(

HDC hdc, // handle to device context

UINT fMode // text-alignment flag

);

说明:该函数设置文本对齐方式。Hdc是参数描述表,fuAlign是文本对齐方式

* GetClientRect函数

语法:

BOOL GetClientRect(

HWND hWnd, // handle to window

LPRECT lpRect // address of structure for client coordinates

);

说明: hWnd是与用户区域相关的窗口,lpRect是指向RECT结构的指针。

附:通过HDC句柄调用GDI函数的三种绘图模式

// DC32.cpp : Defines the entry point for the application.

#include "stdafx.h"

#include "resource.h"

#include <stdio.h>

// GDI函数通过HDC句柄来调用

#if 1

//消息回调函数窗口客户区绘图 BeginPaint(hwndDlg,&ps);窗口刷新时不消失,在WM_PAINT消息下使用,封装成CPaintDC类

BOOL CALLBACK dlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch(uMsg)

{

case WM_COMMAND:

switch(wParam)

{

case IDCANCEL:

EndDialog(hwndDlg,IDCANCEL);

break;

}

case WM_PAINT: //窗口客户区绘图

{

PAINTSTRUCT ps;

//获取与容器客户区关联的绘图句柄

HDC hdc=BeginPaint(hwndDlg,&ps);

//画两个矩形

Rectangle(hdc,0,0,40,40);

Rectangle(hdc,80,80,120,120);

//画一条线

MoveToEx(hdc,20,20,NULL);

LineTo(hdc,100,100);

EndPaint(hwndDlg,&ps);

}

break;

}

return FALSE;

}

#endif

#if 0

//消息回调函数非窗口客户区绘图 ;窗口刷新时不消失,在WM_NCPAINT消息下使用,封装成CWindowDC类

BOOL CALLBACK dlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch(uMsg)

{

case WM_COMMAND:

switch(wParam)

{

case IDCANCEL:

EndDialog(hwndDlg,IDCANCEL);

break;

}

case WM_NCPAINT:

{

HDC hdc = GetWindowDC(hwndDlg);

RECT rect;

GetWindowRect(hwndDlg,&rect); //获取整个窗口的空间区域

//画两个矩形

Rectangle(hdc,0,0,rect.right-rect.left,rect.bottom-rect.top);

char str[222];

sprintf(str,"非客户空间(%d,%d,%d,%d)",rect.left,rect.top,rect.right,rect.bottom);

SetTextColor(hdc,RGB(255,0,0));

TextOut(hdc,5,5,str,strlen(str));

ReleaseDC(hwndDlg,hdc);

return TRUE;

}

case WM_MOVE:

SendMessage(hwndDlg,WM_NCPAINT,0,0);

InvalidateRect(hwndDlg,NULL,TRUE);

return TRUE;

}

return FALSE;

}

#endif

#if 0

//消息回调函数临时客户区绘图 ;窗口刷新时消失,任何时候都可以使用,封装成GetDC类

BOOL CALLBACK dlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch(uMsg)

{

case WM_COMMAND:

switch(wParam)

{

case IDCANCEL:

EndDialog(hwndDlg,IDCANCEL);

break;

}

case WM_LBUTTONDOWN:

HDC hdc = GetDC(hwndDlg);

int x = LOWORD(lParam);

int y = HIWORD(lParam);

// 画一个圆形

Ellipse(hdc,x-10,y-10,x+10,y+10);

ReleaseDC(hwndDlg,hdc);

return TRUE;

}

return FALSE;

}

#endif

//测试坐标系转换

void ScreenToClient(HWND hwndParent, RECT &rect)

{

POINT lt = {rect.left,rect.top};

POINT rb = {rect.right,rect.bottom};

ScreenToClient(hwndParent,<);

ScreenToClient(hwndParent,&rb);

rect.left = lt.x;

rect.top = rb.x;

rect.bottom = rb.y;

}

#if 0

//消息回调函数测试坐标系转换

BOOL CALLBACK dlgFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch(uMsg)

{

case WM_COMMAND:

switch(wParam)

{

case IDCANCEL:

EndDialog(hwndDlg,IDCANCEL);

break;

}

case WM_SETCURSOR: //当鼠标靠近或者切换到不同的控件时回调

{

HWND hwnd = (HWND)wParam;

if(hwnd != GetDlgItem(hwndDlg,IDOK))

return TRUE;

RECT rect;

GetWindowRect(hwnd,&rect); //获取按钮的屏幕坐标

ScreenToClient(hwndDlg,rect);

rect.left -=100;

rect.right -= 100;

MoveWindow(hwnd,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE);

return TRUE;

}

break;

}

return FALSE;

}

#endif

int APIENTRY WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow)

{

//MessageBox(NULL,"first program","note",0);

DialogBox(hInstance,(LPCTSTR)ws,NULL,dlgFunc);

return 0;

}

运行效果:

-End-

本页共378段,11602个字符,18420 Byte(字节)